package gui;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.DefaultXYDataset;

import parsing.InvalidFunctionException;
import parsing.Parser;
import ch.randelshofer.quaqua.QuaquaLookAndFeel;
import core.BisectionMethod;
import core.FalsePositionMethod;
import core.Method;
import core.NewtonRaphson;
import core.Point;
import core.SecantMethod;
import functions.Function;
import javax.swing.JScrollPane;

public class Solver extends JFrame {
	public static Solver sol;
	private JPanel contentPane;
	private JTextField textField;
	private DefaultXYDataset dataset = new DefaultXYDataset();
	private ChartPanel cp;
	private Function fun;
	private String method;
	private int noOfIterations = -1;
	private double precesion = -1;
	private double x1 = -1;
	private double x2 = -1;
	private ArrayList<Point> roots = new ArrayList<Point>();
	private int counter = 0;
	private File file;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Solver frame = new Solver("");
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Solver(final String method) {
		setUndecorated(true);
		setBounds(100, 100, 753, 491);
		this.method = method;

		contentPane = new JPanel();
		contentPane.setBorder(new TitledBorder(null, "Numerical Beast",
				TitledBorder.CENTER, TitledBorder.ABOVE_TOP, null, null));
		contentPane.setBackground(Color.lightGray);
		setContentPane(contentPane);
		contentPane.setLayout(null);

		JFreeChart chart = ChartFactory.createXYLineChart(
				"Finding roots of function", // The chart title
				"X", // x axis label
				"Y", // y axis label
				dataset, // The dataset for the chart
				PlotOrientation.VERTICAL, true, // Is a legend required?
				false, // Use tooltips
				false // Configure chart to generate URLs?
				);
		cp = new ChartPanel(chart);
		cp.setLocation(10, 81);
		cp.setSize(583, 358);
		getContentPane().add(cp);

		try {
			UIManager.setLookAndFeel(new QuaquaLookAndFeel());
		} catch (UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		}
		textField = new JTextField();
		textField.setBounds(32, 28, 711, 31);
		contentPane.add(textField);
		textField.setColumns(10);
		textField.addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent arg0) {
			}

			@Override
			public void keyReleased(KeyEvent arg0) {
			}

			@Override
			public void keyPressed(KeyEvent key) {
				if (key.getKeyCode() == KeyEvent.VK_ESCAPE) {
					setBounds(100, 100, 753, 491);
					cp.setLocation(10, 81);
					cp.setSize(583, 358);
				}
			}
		});

		JLabel lblNewLabel = new JLabel("f(x)");
		lblNewLabel.setBounds(10, 31, 18, 24);
		contentPane.add(lblNewLabel);

		JButton btnSolve = new JButton("Solve");
		btnSolve.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String txt = textField.getText();
				roots = new ArrayList<Point>();
				try {
					fun = new Parser().parse(txt);
				} catch (InvalidFunctionException e1) {
					JOptionPane.showMessageDialog(null, e1.getMessage());
					return;
				}
				Method f;
				long start,end;

				if (method.equals("b")) {
//					start = System.nanoTime();
					f = new BisectionMethod(noOfIterations, precesion, x1, x2);
//					end = System.nanoTime();
				} else if (method.equals("s")) {
//					start = System.nanoTime();
					f = new SecantMethod(noOfIterations, precesion, x1, x2);
//					end  = System.nanoTime();
				} else if (method.equals("f")) {
//					start = System.nanoTime();
					f = new FalsePositionMethod(noOfIterations, precesion, x1,
							x2);
//					end  = System.nanoTime();
				} else
				{
//					start  = System.nanoTime();
					f = new NewtonRaphson(noOfIterations, precesion, x1);
//					end  = System.nanoTime();
				}
				try {
					start = System.nanoTime();
					roots = f.run(fun);
					System.out.println(roots);
					end = System.nanoTime();

				} catch (Exception e1) {
					JOptionPane.showMessageDialog(null, e1.getMessage());
					return;
				}

				double maxX = Double.MIN_VALUE;
				double minX = Double.MAX_VALUE;

				double step = 0.001;

				for (Point p : roots) {
					minX = Math.min(p.getX(), minX);
					maxX = Math.max(p.getX(), maxX);
				}

				minX -= 1;
				maxX += 1;
				double[][] values = new double[2][(int) ((maxX - minX) / step)];

				int i = 0;
				for (double x = minX; x <= maxX && i < values[0].length; x += step, i++) {
					values[0][i] = x;
					values[1][i] = fun.eval(x);
				}
				dataset.addSeries("Function", values);
				JScrollPane scrollPane = new JScrollPane();
				scrollPane.setBounds(613, 286, 111, 109);
				contentPane.add(scrollPane);

				JList list = new JList();
				scrollPane.setViewportView(list);
				Point[] p = new Point[roots.size()];
				roots.toArray(p);
				list.setListData(p);
				JOptionPane.showMessageDialog(null, "Error Bound " + f.getErrorBound()
						+ "\nNumber Of Iterations " + roots.size()
						+ "\n at " + (end - start) +" ms");

			}
		});
		btnSolve.setBounds(603, 82, 140, 29);
		contentPane.add(btnSolve);

		JButton btnInterval = new JButton("Specify Interval");
		btnInterval.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					if (method.equals("n")) {
						String s = JOptionPane.showInputDialog("Enter x", "x");
						x1 = Double.parseDouble(s);
					} else {
						String s = JOptionPane.showInputDialog("Enter x1,x2",
								"x1,x2");
						String[] dos = s.split(",");
						x1 = Double.parseDouble(dos[0]);
						x2 = Double.parseDouble(dos[1]);
					}
				} catch (Exception x) {
					JOptionPane.showMessageDialog(null, "Invalid Input");
				}
			}
		});
		btnInterval.setBounds(603, 123, 140, 29);
		contentPane.add(btnInterval);

		JButton btnLoad = new JButton("Load from file");
		btnLoad.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					JFileChooser chooser = new JFileChooser();
					String url = "";
					int returnVal = chooser.showOpenDialog(chooser);
					if (returnVal == JFileChooser.APPROVE_OPTION) {
						url = chooser.getSelectedFile().getPath();
					}
					file = new File(url);
					String txt = new Scanner(file).nextLine();
					System.out.println(txt);
					textField.setText(txt);
					roots = new ArrayList<Point>();
					try {
						fun = new Parser().parse(txt);
					} catch (InvalidFunctionException e1) {
						JOptionPane.showMessageDialog(null, e1.getMessage());
						return;
					}
					Method f;
					if (method.equals("b")) {
//						start = System.currentTimeMillis();
						f = new BisectionMethod(noOfIterations, precesion, x1,
								x2);
//						end = System.currentTimeMillis();
//						JOptionPane.showMessageDialog(null, "Error Bound " + f.getErrorBound()
//								+ "\nNumber Of Iterations" + roots.size()
//								+ "\n at" + (end - start));
						
					} else if (method.equals("s")) {
//						start = System.currentTimeMillis();
						f = new SecantMethod(noOfIterations, precesion, x1, x2);
//						end = System.currentTimeMillis();
						

						
					} else if (method.equals("f")) {
//						start = System.currentTimeMillis();
						f = new FalsePositionMethod(noOfIterations, precesion,
								x1, x2);
//						end = System.currentTimeMillis();
						
					} else
					{
//						start = System.currentTimeMillis();
						f = new NewtonRaphson(noOfIterations, precesion, x1);
//						end = System.currentTimeMillis();

					}
					try {
						roots = f.run(fun);
						
					} catch (Exception e1) {
						JOptionPane.showMessageDialog(null, e1.getMessage());
						return;
					}

					double maxX = Double.MIN_VALUE;
					double minX = Double.MAX_VALUE;

					double step = 0.001;

					for (Point p : roots) {
						minX = Math.min(p.getX(), minX);
						maxX = Math.max(p.getX(), maxX);
					}

					minX -= 1;
					maxX += 1;
					double[][] values = new double[2][(int) ((maxX - minX) / step)];

					int i = 0;
					for (double x = minX; x <= maxX && i < values[0].length; x += step, i++) {
						values[0][i] = x;
						values[1][i] = fun.eval(x);
					}
					dataset.addSeries("Function", values);
					JScrollPane scrollPane = new JScrollPane();
					scrollPane.setBounds(613, 286, 111, 109);
					contentPane.add(scrollPane);
//					JOptionPane.showMessageDialog(null, "Error Bound " + f.getErrorBound()
//							+ "\nNumber Of Iterations" + roots.size()
//							+ "\n at" + (end - start));

					JList list = new JList();
					scrollPane.setViewportView(list);
					Point[] p = new Point[roots.size()];
					roots.toArray(p);
					list.setListData(p);
				} catch (Exception e1) {
					// TODO: handle exception
				}
			}
		});
		btnLoad.setBounds(603, 164, 140, 29);
		contentPane.add(btnLoad);

		JButton btnSimulate = new JButton("Simulate");
		btnSimulate.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					counter = 0;
					if (method.equals("b") || method.equals("f")) {
						double[] d1 = new double[] {
								roots.get(counter * 2).getX(),
								roots.get(counter * 2).getX() };
						double[] d2 = new double[] { 10, -10 }; // TODO change
						double data1[][] = { d1, d2 };
						dataset.addSeries("t1", data1);

						double[] d11 = new double[] {
								roots.get(counter * 2 + 1).getX(),
								roots.get(counter * 2 + 1).getX() };
						double[] d22 = new double[] { 10, -10 }; // TODO change
						double data2[][] = { d11, d22 };
						dataset.addSeries("t2", data2);

						System.out.println(counter * 2 + "  "
								+ (counter * 2 + 1));
						System.out.println((roots.get(counter * 2).getX())
								+ "   " + roots.get(counter * 2 + 1).getX());
					} else if (method.equals("s") || method.equals("n")) {
						double[] d1 = new double[] { roots.get(counter).getX(),
								roots.get(counter).getX() };
						double[] d2 = new double[] { 10, -10 }; // TODO change
						double data1[][] = { d1, d2 };
						dataset.addSeries("t1", data1);
					}
				} catch (Exception ee) {
				}
			}

		});
		btnSimulate.setBounds(603, 410, 140, 29);
		contentPane.add(btnSimulate);

		JButton button_1 = new JButton(">");
		button_1.setBounds(679, 451, 64, 29);
		contentPane.add(button_1);
		button_1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					if (method.equals("b") || method.equals("f")) {
						double[] d1 = new double[] {
								roots.get(counter * 2).getX(),
								roots.get(counter * 2).getX() };
						double[] d2 = new double[] { 10, -10 }; // TODO change
						double data1[][] = { d1, d2 };
						dataset.addSeries("t1", data1);

						double[] d11 = new double[] {
								roots.get(counter * 2 + 1).getX(),
								roots.get(counter * 2 + 1).getX() };
						double[] d22 = new double[] { 10, -10 }; // TODO change
						double data2[][] = { d11, d22 };
						dataset.addSeries("t2", data2);

						System.out.println(counter * 2 + "  "
								+ (counter * 2 + 1));
						System.out.println((roots.get(counter * 2).getX())
								+ "   " + roots.get(counter * 2 + 1).getX());
					} else if (method.equals("s") || method.equals("n")) {
						double[] d1 = new double[] { roots.get(counter).getX(),
								roots.get(counter).getX() };
						double[] d2 = new double[] { 10, -10 }; // TODO change
						double data1[][] = { d1, d2 };
						dataset.addSeries("t1", data1);
					}
					if (counter * 2 < roots.size() - 3) {
						counter++;
					}
				} catch (Exception ee) {
				}
			}
		});

		JButton button_2 = new JButton("<");
		button_2.setBounds(603, 451, 64, 29);
		contentPane.add(button_2);
		button_2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					if (method.equals("b") || method.equals("f")) {
						double[] d1 = new double[] {
								roots.get(counter * 2).getX(),
								roots.get(counter * 2).getX() };
						double[] d2 = new double[] { 10, -10 }; // TODO change
						double data1[][] = { d1, d2 };
						dataset.addSeries("t1", data1);

						double[] d11 = new double[] {
								roots.get(counter * 2 + 1).getX(),
								roots.get(counter * 2 + 1).getX() };
						double[] d22 = new double[] { 10, -10 }; // TODO change
						double data2[][] = { d11, d22 };
						dataset.addSeries("t2", data2);

						System.out.println(counter * 2 + "  "
								+ (counter * 2 + 1));
						System.out.println((roots.get(counter * 2).getX())
								+ "   " + roots.get(counter * 2 + 1).getX());
					} else if (method.equals("s") || method.equals("n")) {
						double[] d1 = new double[] { roots.get(counter).getX(),
								roots.get(counter).getX() };
						double[] d2 = new double[] { 10, -10 }; // TODO change
						double data1[][] = { d1, d2 };
						dataset.addSeries("t1", data1);
					}
					if (counter > 0)
						counter--;
				} catch (Exception ee) {
				}
			}
		});

		JButton btnBack = new JButton("Back");
		btnBack.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				sol.setVisible(false);
				Main.main.setVisible(true);
			}
		});
		btnBack.setBounds(10, 451, 93, 29);
		contentPane.add(btnBack);

		JButton btnExit = new JButton("Exit");
		btnExit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		btnExit.setBounds(121, 451, 93, 29);
		contentPane.add(btnExit);

		JButton btnSpecify = new JButton("Specify precision");
		btnSpecify.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					String s = JOptionPane.showInputDialog(
							"Enter the precision", "p");
					precesion = Double.parseDouble(s);
				} catch (Exception e1) {
					// TODO: handle exception
					JOptionPane.showMessageDialog(null, "Invalid Input");
				}
			}
		});
		btnSpecify.setBounds(603, 205, 140, 29);
		contentPane.add(btnSpecify);

		JButton btnSpecifyNumberIterations = new JButton("Specify Iterations");
		btnSpecifyNumberIterations.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					String s = JOptionPane.showInputDialog(
							"Enter the number of iterations", "i");
					noOfIterations = Integer.parseInt(s);
				} catch (Exception e1) {
					// TODO: handle exception
					JOptionPane.showMessageDialog(null, "Invalid Input");
				}

			}
		});
		btnSpecifyNumberIterations.setBounds(603, 246, 140, 29);
		contentPane.add(btnSpecifyNumberIterations);

		JButton btnFullScreen = new JButton("Full Screen");
		btnFullScreen.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				setExtendedState(JFrame.MAXIMIZED_BOTH);
				cp.setSize(getSize());
				cp.setLocation(0, 0);
				contentPane.setFocusable(true);
			}
		});

		btnFullScreen.setBounds(427, 454, 121, 23);
		contentPane.add(btnFullScreen);

	}
}
